package com.gwtgeo.ui.client.mvp.layers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.customware.gwt.presenter.client.EventBus;

import org.gwtopenmaps.openlayers.client.layer.Layer;
import org.gwtopenmaps.openlayers.client.layer.WMS;
import org.gwtopenmaps.openlayers.client.layer.WMSParams;

import com.google.inject.Inject;
import com.gwtgeo.service.model.WMSLayerDef;
import com.gwtgeo.ui.client.defbuilder.DefBuilder;
import com.gwtgeo.ui.client.defbuilder.ListDefBuilder;
import com.gwtgeo.ui.client.defbuilder.WMSLayerDefBuilder;
import com.gwtgeo.ui.client.mvp.HasValues;
import com.gwtgeo.ui.client.mvp.Saveable;
import com.gwtgeo.ui.client.mvp.windowsmanager.WinModalDataNames;
import com.gwtgeo.ui.client.mvp.windowsmanager.WindowTypeEnum;
import com.gwtgeo.ui.client.mvp.windowsmanager.WindowedDisplay;
import com.gwtgeo.ui.client.mvp.windowsmanager.WindowedPresenter;
import com.gwtgeo.ui.shared.event.AddLayerEvent;
import com.gwtgeo.ui.shared.event.OpenWinModalEvent;
import com.gwtgeo.ui.shared.event.RemoveLayerEvent;
import com.gwtgeo.ui.shared.event.RepositionLayerEvent;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.events.HasClickHandlers;
import com.smartgwt.client.widgets.grid.events.CellClickEvent;
import com.smartgwt.client.widgets.grid.events.CellClickHandler;
import com.smartgwt.client.widgets.grid.events.ChangedEvent;
import com.smartgwt.client.widgets.grid.events.ChangedHandler;
import com.smartgwt.client.widgets.grid.events.HasCellClickHandlers;
import com.smartgwt.client.widgets.grid.events.HasChangedHandlers;
import com.smartgwt.client.widgets.grid.events.HasRecordDropHandlers;
import com.smartgwt.client.widgets.grid.events.RecordDropEvent;
import com.smartgwt.client.widgets.grid.events.RecordDropHandler;

public class LayersPresenter extends WindowedPresenter<LayersPresenter.Display>
		implements Saveable<List<WMSLayerDef>, List<WMS>> {

	private static int REMOVE_COL_NUM = 4;

	private Map<String, Layer> layers;

	private DefBuilder<List<WMSLayerDef>, List<WMS>> defBuilder;

	public interface Display extends WindowedDisplay {

		HasClickHandlers getAddLayerClick();

		HasValues<WMSLayerDef> getLayers();

		HasChangedHandlers getLayerVisibilityChange();

		HasCellClickHandlers getLayerRemove();

		HasRecordDropHandlers getLayerReposition();

		int getRowNumber(Record record);

	}

	@Override
	public WindowTypeEnum getType() {
		return WindowTypeEnum.LAYERS;
	}

	@Inject
	public LayersPresenter(Display display, EventBus eventBus) {
		super(display, eventBus);
		this.defBuilder = new ListDefBuilder<WMSLayerDef, WMS>(
				new WMSLayerDefBuilder());
	}

	@Override
	public void init(Map<String, Object> params) {
		this.layers = new HashMap<String, Layer>();
		this.display.getLayers().clear();
	}

	@Override
	protected void onBind() {
		super.onBind();
		this.registerHandler(this.display.getAddLayerClick().addClickHandler(
				new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						eventBus.fireEvent(new OpenWinModalEvent(
								WindowTypeEnum.ADD_LAYER));
					}
				}));

		this.registerHandler(this.display.getLayerVisibilityChange()
				.addChangedHandler(new ChangedHandler() {

					@Override
					public void onChanged(ChangedEvent event) {
						WMSLayerDef layerDef = (WMSLayerDef) display
								.getLayers().getRowValue(event.getRowNum());
						if (layerDef != null) {
							Layer layer = layers.get(layerDef.getId());
							layer.setIsVisible((Boolean) event.getItem()
									.getValue());
						}
					}
				}));
		this.registerHandler(this.display.getLayerRemove().addCellClickHandler(
				new CellClickHandler() {

					@Override
					public void onCellClick(CellClickEvent event) {
						int colNum = event.getColNum();
						if (colNum == LayersPresenter.REMOVE_COL_NUM) {
							WMSLayerDef layerDef = (WMSLayerDef) display
									.getLayers().getRowValue(event.getRowNum());
							if (layerDef != null) {
								eventBus.fireEvent(new RemoveLayerEvent(layers
										.get(layerDef.getId())));
								layers.remove(layerDef.getId());
							}
						}
					}
				}));
		this.registerHandler(this.display.getLayerReposition()
				.addRecordDropHandler(new RecordDropHandler() {

					@Override
					public void onRecordDrop(RecordDropEvent event) {
						Record[] records = event.getDropRecords();
						if (records != null && records.length > 0) {
							int position = display.getRowNumber(records[0]);
							if (event.getIndex() > position + 1) {
								eventBus.fireEvent(new RepositionLayerEvent(
										layers.get(records[0]
												.getAttribute("id")), layers
												.size()
												- (event.getIndex())));
							} else if (position > event.getIndex()) {
								eventBus.fireEvent(new RepositionLayerEvent(
										layers.get(records[0]
												.getAttribute("id")), layers
												.size()
												- event.getIndex() - 1));
							}
						}

					}
				}));

	}

	@Override
	public void retrieveWinData(WindowTypeEnum source,
			Map<String, Object> data) {
		if (source.equals(WindowTypeEnum.ADD_LAYER)) {
			String layerName = (String) data.get(WinModalDataNames.LAYER_NAME);
			String layerUrl = (String) data.get(WinModalDataNames.LAYER_URL);
			String layerParams = (String) data
					.get(WinModalDataNames.LAYER_PARAMS);

			WMSParams params = new WMSParams();
			params.setLayers(layerParams);
			params.setFormat("image/png");
			params.setIsTransparent(true);

			WMS layer = new WMS(layerName, layerUrl, params);
			layer.setIsBaseLayer(false);
			layer.setIsVisible(true);

			layers.put(layer.getId(), layer);

			WMSLayerDef layerDef = new WMSLayerDef();
			layerDef.setId(layer.getId());
			layerDef.setIsBaseLayer(false);
			layerDef.setFormat("image/png");
			layerDef.setIsTransparent(true);
			layerDef.setIsVisble(true);
			layerDef.setLayers(layerParams);
			layerDef.setName(layerName);
			layerDef.setUrl(layerUrl);
			display.getLayers().addFirstValue(layerDef);

			this.eventBus.fireEvent(new AddLayerEvent(layer));
		}
	}

	@Override
	public List<WMS> load(List<WMSLayerDef> defs) {
		// Clean old layers.
		this.layers = new HashMap<String, Layer>();
		this.display.getLayers().clear();
		List<WMS> layers = this.defBuilder.buildObj(defs, null);
		for (int i=0;i<layers.size();i++) {
			WMS layer = layers.get(i);
			WMSLayerDef def = defs.get(i);
			this.layers.put(layer.getId(), layer);
			def.setId(layer.getId());
			display.getLayers().addFirstValue(def);
		}
		
		return layers;
	}

	@Override
	public List<WMSLayerDef> buildSaveableModel() {
		List<WMSLayerDef> reversed = this.display.getLayers().getValues();
		List<WMSLayerDef> layers = new ArrayList<WMSLayerDef>();
		for (int i = reversed.size() - 1; i >= 0; i--) {
			layers.add(reversed.get(i));
		}
		return layers;
	}

	@Override
	protected void onRevealDisplay() {
	}

	@Override
	protected void onUnbind() {
	}

	protected DefBuilder<List<WMSLayerDef>, List<WMS>> getDefBuilder() {
		return defBuilder;
	}
	
	

}
